Skip to content

fix(ui): prevent UI freeze during context compaction#174

Merged
laynepenney merged 4 commits intomainfrom
fix/compaction-ui-freeze
Jan 26, 2026
Merged

fix(ui): prevent UI freeze during context compaction#174
laynepenney merged 4 commits intomainfrom
fix/compaction-ui-freeze

Conversation

@laynepenney
Copy link
Copy Markdown
Collaborator

Summary

  • Add onCompaction callback to AgentOptions to notify UI when compaction starts/ends
  • Yield to event loop with setImmediate after callback to allow UI to render
  • Wire up callback in index.ts to show spinner and update Ink UI activity status

Problem

The UI would freeze and key events would be blocked during context compaction because:

  1. The UI wasn't notified that compaction was happening
  2. Synchronous operations before the async API call blocked the event loop

Solution

  • Added onCompaction?: (status: 'start' | 'end') => void callback
  • Added await new Promise(resolve => setImmediate(resolve)) to yield control back to the event loop
  • UI now shows "compacting context" spinner/status during compaction

Test plan

  • Run codi with --context-window 4000 to trigger compaction sooner
  • Fill context with conversation until compaction triggers
  • Verify UI shows "compacting" status instead of freezing
  • Verify key events (Ctrl+C, etc.) work during compaction

🤖 Generated with Claude Code

laynepenney and others added 4 commits January 26, 2026 06:42
- Add onCompaction callback to AgentOptions to notify UI when compaction starts/ends
- Yield to event loop with setImmediate after callback to allow UI to render
- Wire up callback in index.ts to show spinner and update Ink UI activity status
- Fixes blocked key events during compaction by allowing event loop to process

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add debouncing (16ms/~60fps) to setStatus() to coalesce rapid updates
- Add batching for appendToMessage() to reduce streaming re-renders
- Add flush() method to immediately emit pending updates (useful for tests)
- Update tests to account for debounced behavior

This reduces re-renders during high-frequency updates (streaming, tool calls)
which was causing key events to be dropped intermittently.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The spinner animation (updating every 120ms) was causing the entire
InkApp component to re-render, which led to dropped key events. Users
had to press keys 5-6 times before they registered.

Fix: Extract spinner animation into a separate ActivityPanel component
that manages its own state. This prevents the 120ms state updates from
triggering full app re-renders:

- Create ActivityPanel component with isolated spinner state
- Move hasActiveProgress/spinnerFrame state into ActivityPanel
- Remove spinner from statusLines (already shown in ActivityPanel)
- Use simplified scroll info calculation that doesn't depend on spinner

The parent InkApp now only re-renders when actual data changes, not
every 120ms for spinner animation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@laynepenney laynepenney merged commit 37ab092 into main Jan 26, 2026
3 checks passed
@laynepenney laynepenney deleted the fix/compaction-ui-freeze branch January 26, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant